/******************************************************************************
GameConnection.cpp
 
   -Implements the GameConnection class, which is the link between
    the connection to the client and the game logic (guts)

Copyright (c) 2005 - Realtime Gaming.  All rights reserved.
******************************************************************************/

#include <windows.h>

#include "lib.h"
#include "kenoserv.h"
#include "GameConnection.h"
#include "GameGuts.h"

#include "endian.h"

#include "log.h"
#include "opts.h"
#include "stdncerr.h"
#include "badpacketmsgs.h"

#include "kenoproto.h"
#include "tracksql.h"
#include "KenoXMLTranslator.h"
#include "XMLTranslator.h"

#define APP_1 'K'
#define APP_2 'N'

/******************************************************************************
Construction / Desctruciton
******************************************************************************/
GameConnection::GameConnection(Server* pServer, SOCKET client)
    : super( pServer, client )
{
    guts = NULL;
	loggedOn = 0;
    casinoSpecific = NULL;

    // kharmon.  11-16-06.  #16921.
    msglen = 0;
    memset(msgbuf,0,sizeof(msgbuf));
}

GameConnection::~GameConnection()
{
    // CSN-7075: New Authentication
	trackLogoutSession();
	
	if ( guts )
    {
        delete guts;
        guts = NULL;
    }

    removeCasinoSpecific();
}

/******************************************************************************
Methods
******************************************************************************/

/******************************************************************************
Function name	: GameConnection::getInfo
Description	    : Gets information about the player
Return type		: pInfo - structure holding the information
******************************************************************************/
pInfo  
GameConnection::getInfo() const
{
    return casinoSpecific;
}

/******************************************************************************
Function name	: GameConnection::getResponse
Description	    : The main message loop for the server.
Return type		: bool8 - Whether or not the message was processed.
******************************************************************************/
bool8 
GameConnection::getResponse()
{
	int32 ieat = 0, remain = iused, len;
	uint8* src = ibuf;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	while (remain > 0)
	{
		if (remain < src[0])
			break;

		// Handle an invalidly formatted message of length zero.
		if (src[0] <= 1)    
		{
			ieat += 1;
			remain -= 1;
			src += 1;
			continue;
		}

        // kharmon.  11-16-06.  #16921.
        msglen = 0;
        memset(msgbuf,0,sizeof(msgbuf));

		len = decode(src + 1, msgbuf, src[0] - 1);
		ieat += src[0];
		remain -= src[0];
		src += src[0];

		switch (msgbuf[0])
		{
			case 'L':
			{
                handleANSILogin(len);
                break;
			}

            // Multililngual Login
            case 'M':
            {
				switch (msgbuf[1])
				{
					case 'L':
                    {
                        handleUnicodeLogin(len);
                        break;
                    }
                    default:
                    {
                        handleUnknownMessage(len);
                        break;
                    }
                }

                break;
            }

			case 'B':  // Bet Message - guts specific
			{
                handleBetMessage(len);
				break;
			}

            case 'S':   //Subgame message - guts specific
            {
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

                handleSubGameMessage(len);
                break;
            }

            case 'm':  // Balance Request
            {
				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] != last_message && ValidateSession())
	                handleBalanceRequest(len);
	            break;
            }

            case 'x':   //Multiplier Response - guts specific
            {
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

                handleMultiplierMessage(len);
                break;
            }

			default:
            {
                handleUnknownMessage(len);
				break;
            }

		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

/******************************************************************************
Function name	: GameConnection::handleUnknownMessage
Description	    : Called to handle messages we don't know about.
                  Responds with "Invalid"
Return type		: void 
Argument        : int32 nMsgLen - The length of the message buffer
******************************************************************************/
void
GameConnection::handleUnknownMessage(int32 nMsgLen)
{
    trackaddBadPacket(GETREPONSE_DEFAULT_CASE,msgbuf[0],(char*)msgbuf,nMsgLen);

    Log::msgWarning("Connection::getResponse:  Unknown input"
	    " (0x%02x)  ", msgbuf[0]);

    respondInvalid();
}

/******************************************************************************
Function name	: GameConnection::handleUnicodeLogin
Description	    : Parses and processes Unicode logins
Return type		: void 
Argument        : int32 nMsgLen - length of the message buffer
******************************************************************************/
void
GameConnection::handleUnicodeLogin(int32 nMsgLen)
{
    int32 err = 0;

    if (!parseUnicodeCasinoLogin(&err))
    {
	    msgStart();
	    msgAdd((uint8)'E');
	    msgAdd((uint8)(err & 0xff));
	    msgEnd();
	    return;
    }

    handleLogin(nMsgLen);
}

/******************************************************************************
Function name	: GameConnection::handleANSILogin
Description	    : Parses and processes ANSI logins
Return type		: void 
Argument        : int32 nMsgLen - length of the message buffer
******************************************************************************/
void
GameConnection::handleANSILogin(int32 nMsgLen)
{
    int32 err = 0;

    if (!parseAnsiCasinoLogin(&err))
    {
	    msgStart();
	    msgAdd((uint8)'E');
	    msgAdd((uint8)(err & 0xff));
	    msgEnd();
	    return;
    }

    handleLogin(nMsgLen);
}

/******************************************************************************
Function name	: GameConnection::handleLogin
Description	    : Performs server and game specific login processing once
                  the player's account has been verified
Return type		: void 
Argument        : int32 nMsgLen - length of the message buffer
******************************************************************************/
void GameConnection::handleLogin(int32 nMsgLen)
{
    if (isServerStopping())
    {
	    sendStopMsg();
	    return;
    }

    if (!loggedOn)
    {
        //We assume this is here, since it's created as part of
        //"phase 1" of processing the login
        ASSERT ( guts );

		int32 balance = 0;

	    // no player limits for keno - but we need to get the compFactor
        int32 compFactor = 0;

	    if (!trackGetPlayerLimits(&compFactor)
			|| !trackGetBalance(&balance) )
	    {
		    Log::msgWarning("can't get player limits or balance!");
            respondInvalid();
		    return;
	    }

        guts->setCompFactor( compFactor );

		//psharpless  11/3/06  cases#: 9360 & 16436
		//Bonus Bingo and Keno denomination amounts are no longer hard coded

		if(trackGetDenominations() != TrackSQL::NoError)
		{
			Log::msgWarning("Can't get the game denominations!");
            respondInvalid();
		    return;
		}

#ifdef FORCED_KENO
		// CSN-12127 - Load forced results only if Testing is enabled
		if (ServerOpts::bAllowTesting)
		{
			guts->loadForcedResults();
		}
#endif

	    msgStart();
	    msgAdd((uint8)'L');
	    msgAdd(balance);

		msgAdd((int32)guts->getMinBet());
		msgAdd((int32)guts->getMaxBet());
        
        //Add the maximum number of cards.
        msgAdd(guts->getMaxCards());        

        //Give the guts a chance to append game-specific info to the
        //server's login response.
        msgAdd(guts->getPaytable());

		msgAdd(guts->getDenomValue(0));
		msgAdd(guts->getDenomValue(1));
		msgAdd(guts->getDenomValue(2));
		msgAdd(guts->getDenomValue(3));
		msgAdd(guts->getDenomValue(4));

		msgEnd();

        if ( guts->getSavedGame() )
        {
            guts->sendSavedGame();
        }

	    loggedOn = true;
    }
    else
    {
	    Log::msgWarning("Login: attempt to re-log on  ");
	    trackaddBadPacket(TRY_LOGON_WHEN_LOGGED_ON,msgbuf[0],(char*)msgbuf,nMsgLen);
        respondInvalid();
    }
}

/******************************************************************************
Function name	: GameConnection::handleBalanceRequest
Description	    : Processes balance requests from the client by returning the
                  balance response with the player's balance
Return type		: void 
Argument        : int32 nMsgLen - length of the message buffer
******************************************************************************/
void 
GameConnection::handleBalanceRequest(int32 nMsgLen)
{    
    int32 balance;
    
    if (isServerStopping())
    {
        sendStopMsg();
        return;
    }
    
    //rule1: gotta be logged on
    if( loggedOn && trackGetBalance(&balance) )
    {
        msgStart();
        msgAdd((uint8)'m');
        msgAdd((int32)balance);
        msgEnd();
    }
}

/******************************************************************************
Function name	: GameConnection::handleBetMessage
Description	    : Forwards the player's bet on to the guts.  This signifies the
                  start of the game.
Return type		: void 
Argument        : int32 nMsgLen - length of the message buffer
******************************************************************************/
void GameConnection::handleBetMessage(int32 nMsgLen)
{
    if (!loggedOn)
	{
		Log::msgWarning("Bet: not logged on...invalid bet  ");

		trackaddBadPacket(TRY_BET_NOT_LOGGED_ON,msgbuf[0],(char*)msgbuf,nMsgLen);
        
        respondInvalidBet();

		return;
	}

	if (isServerStopping())
	{
		sendStopMsg();
		return;
	}

	// [AO 2008-01-16] HA: verify the user is still logged on
	if (!ValidateSession())
		return;

	int32 i;

	uint8 num_cards;
	uint8 num_games;

	i = 1;
	num_cards = *(msgbuf + i);
	i++;

	num_games = *(msgbuf + i);
	i++;

	if((0 == num_cards) || (num_games > guts->getMaxGames()))
	{
		trackaddBadPacket(TRY_PLAY_ZERO_CARDS,msgbuf[0],(char*)msgbuf,nMsgLen);

		msgStart();
		msgAdd((uint8)'C');
		msgEnd();

		return;

	}
	else if ( num_cards > guts->getMaxCards() )
	{
		trackaddBadPacket(TRY_PLAY_MORE_ONE_CARDS,msgbuf[0],(char*)msgbuf,nMsgLen);

		msgStart();
		msgAdd((uint8)'C');
		msgEnd();

		return;
	}

	guts->processBet (msgbuf + i, nMsgLen, num_cards, num_games);
}

/******************************************************************************
Function name	: GameConnection::handleMultiplierMessage
Description	    : Informs the GameGuts of the client's response to winning a
                  multiplier.
Return type		: void 
Argument        : int32 nMsgLen - the length of the message buffer
******************************************************************************/
void GameConnection::handleMultiplierMessage(int32 nMsgLen)
{
    if ( guts->allowsMultipliers() )
    {
        guts->processMultiplierResponse(msgbuf + 1, nMsgLen);
    }
    else
    {
        respondInvalid();
    }
}


/******************************************************************************
Function name	: GameConnection::handleSubGameMessage
Description	    : Forwards a Sub game response to the guts, if the guts is
                  prepared for it.
Return type		: void 
Argument        : int32 nMsgLen - the length of the message buffer
******************************************************************************/
void GameConnection::handleSubGameMessage(int32 nMsgLen)
{
    if ( guts->hasSubGame() )
    {
		guts->processSubGameMessage();
    }
    else
    {
        respondInvalid();
    }
}
/******************************************************************************
Function name	: GameConnection::removeCasinoSpecific
Description	    : Cleans up the "casinoSpecific" memory
Return type		: void
******************************************************************************/
void GameConnection::removeCasinoSpecific()
{
	delete casinoSpecific;

	casinoSpecific = NULL;
}

/******************************************************************************
Message buffer manipulation helpers
******************************************************************************/
void
GameConnection::msgStart()
{
	msglen = 0;
}

void
GameConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

void
GameConnection::msgAdd(int8 SignedByte)
{
	msgbuf[msglen++] = (uint8)((SignedByte) & 0xff);
}

void
GameConnection::msgAdd(int32 SignedLong)
{
	msgbuf[msglen++] = (uint8)((SignedLong) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 24) & 0xff);
}

uint8*
GameConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
GameConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
GameConnection::msgEnd()
{
	msglen = encode(msgbuf, obuf + oused + 1, msglen);
	msglen++;	// include msg len byte
	obuf[oused] = (uint8)(msglen & 0xff);
	oused += msglen;
}


int32
GameConnection::msgRemove(uint8* Src, int32* pResult)
{
	*pResult = (int32)(
		(Src[0] & 0xFF) |
		((Src[1] << 8) & 0xFF00) |
		((Src[2] << 16) & 0xFF0000) |
		((Src[3] << 24) & 0xFF000000));

	return 4;
}

int32
GameConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
GameConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

/******************************************************************************
Function name	: GameConnection::parseAnsiCasinoLogin
Description	    : Performs "phase 1" login processing, which includes parsing
                  the message, creating the GameGuts instance, and validating
                  the user.  For ANSI client logins.
Return type		: bool8 - true if the message is well-formed, player ID is
                  valid and all the required resources were allocated.
Argument        : int32* Err - if non-zero, the cause of failure.
******************************************************************************/
bool8
GameConnection::parseAnsiCasinoLogin(int32* Err)
{
	Info* info;
	int32 i = 0;

    // First byte should be L for login
    ASSERT( msgbuf[i] == 'L' ); 
    
    //Ok, skip it now that we know it's right
    i++;

	if (msgbuf[i++] != APP_1			|| msgbuf[i++] != APP_2 ||
        msgbuf[i++] != PROTO_VERSION1	|| msgbuf[i++] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c.%c%c",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

    //And here, which game are they wanting to play
    //Set a GameGuts* for the specific game
    guts = GameGuts::CreateGuts( msgbuf[i++], this );

	info = new Info();

	if (!guts || !info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}


    char login[PIDLEN + 1];
	memcpy(login, &msgbuf[i], PIDLEN);
	login[PIDLEN] = '\0';
	strlwr(login);
	i += PIDLEN;

	info->login = login;

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		g_tracker->lock();
		result = g_tracker->getAvailableFunID( info->login );
		g_tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}
	else
	{		
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = g_tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
  
	// Create RTG Session
	if (g_tracker)
	{
		bool8 result;
		g_tracker->lock();
		result = g_tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		g_tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}    

	info->pid = info->login;

	/*
	if (g_tracker)
	{
		int32 result = TrackSQL::ExecFailed;

		g_tracker->lock();
		result = g_tracker->validateLogin(info->login, info->sessionId, info->pid);
		g_tracker->unlock();

		if (result != TrackSQL::NoError)
		{
			if(result == TrackSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == TrackSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

			DEBUGMSG(("Login: attempt to play without logging in: %S",
				(const uint16*) info->login));
			delete info;
			return false;
		}
	}
	*/

	memset(info->gid, 0xff, sizeof(GameID));

	casinoSpecific = info;

	return true;
}
/******************************************************************************
Function name	: GameConnection::parseUnicodeCasinoLogin
Description	    : Performs "phase 1" login processing, which includes parsing
                  the message, creating the GameGuts instance, and validating
                  the user.  For Unicode client logins.
Return type		: bool8 - true if the message is well-formed, player ID is
                  valid and all the required resources were allocated.
Argument        : int32* Err - if non-zero, the cause of failure.
******************************************************************************/
bool8
GameConnection::parseUnicodeCasinoLogin(int32* Err)
{
	Info* info;
	int32 i = 0;

    // First byte should be M for multilingual/unicode
    ASSERT( msgbuf[i] == 'M' ); i++;

    // Second byte should be L for login
    ASSERT( msgbuf[i] == 'L' ); i++;

	if (msgbuf[i++] != APP_1			|| msgbuf[i++] != APP_2 ||
        msgbuf[i++] != PROTO_VERSION1	|| msgbuf[i++] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c.%c%c",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

    //And here, which game are they wanting to play
    //Set a GameGuts* for the specific game
    guts = GameGuts::CreateGuts( msgbuf[i++], this );

	info = new Info();
	if (!guts || !info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

    // Remove PID
	i += info->login.deserialize( (void*) &msgbuf[i] );

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		g_tracker->lock();
		result = g_tracker->getAvailableFunID( info->login );
		g_tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}
	else
	{		
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-7075: New Authentication
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = g_tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
  
	// Create RTG Session
	if (g_tracker)
	{
		bool8 result;
		g_tracker->lock();
		result = g_tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		g_tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}    

	info->pid = info->login;

	/*
	if (g_tracker)
	{
		int32 result = TrackSQL::ExecFailed;

		g_tracker->lock();
		result = g_tracker->validateLogin(info->login, info->sessionId, info->pid);
		g_tracker->unlock();

		if (result != TrackSQL::NoError)
		{
			if(result == TrackSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == TrackSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

			DEBUGMSG(("Login: attempt to play without logging in: %S",
				(const uint16*) info->login));
			delete info;
			return false;
		}
	}
	*/

	memset(info->gid, 0xff, sizeof(GameID));

	casinoSpecific = info;

	return true;
}

/******************************************************************************
Helpers for common messages.
*****************************************************************************/
void
GameConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

void 
GameConnection::respondBadCard(int32 len)
{
    Log::msgWarning("  invalid card  ");
    trackaddBadPacket(INVALID_CARD,msgbuf[0],(char*)msgbuf,len);

    msgStart();
    msgAdd((uint8)'C');
    msgAdd((uint8)0);
    msgEnd();
}

void
GameConnection::respondInvalidBet()
{   
    msgStart();
    msgAdd((uint8)'B');
    msgEnd();
}

void 
GameConnection::respondNoBalance()
{
    msgStart();
	msgAdd((uint8)'$');
    msgEnd();
}

void 
GameConnection::respondGameInProgress()
{
    msgStart();
	msgAdd((uint8)'G');
	msgEnd();
}

void 
GameConnection::respondInvalid()
{
    msgStart();
	msgAdd((uint8)'N');
    msgEnd();
}

/******************************************************************************
Tracking methods
******************************************************************************/
void
GameConnection::trackaddBadPacket(int32 error_reason, char MessageType, char *msgbuf, int32 msglen)
{
	pInfo info = (pInfo)casinoSpecific;

	if (g_tracker && info)
	{
		g_tracker->lock();
		g_tracker->addBadPacket(guts->getGameID(), guts->getMachineID(), sBadPacketMessages[error_reason].message, sBadPacketMessages[error_reason].severity, info->pid, MessageType, (uint8*)msgbuf, msglen);
		g_tracker->unlock();
	}
}

bool8
GameConnection::trackGetBalance( int32 *balance )
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = TrackSQL::ExecFailed;
	int32 comp;
	*balance = 0;

	if (g_tracker && info)
	{
		g_tracker->lock();
		result = g_tracker->getBalance(ServerOpts::casinoId,
			info->pid, info->forMoney, balance, &comp);
		g_tracker->unlock();
	}

	return (bool8)(result == TrackSQL::NoError);
}

int32
GameConnection::trackAdjustBalance(int32 Amount)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = TrackSQL::ExecFailed;
	int32 bal=0, comp=0;

	if (g_tracker && info)
	{
		g_tracker->lock();
		result = g_tracker->adjustBalance(ServerOpts::casinoId,
			info->pid, info->forMoney,
			Amount, &bal, &comp);
		g_tracker->unlock();
	}

	if (result == TrackSQL::NoError)
		return bal;
	return 0;
}

bool8
GameConnection::trackGetPlayerLimits(int32* compFactor)
{
    ASSERT( guts );

	pInfo info = (pInfo)casinoSpecific;

    //We're forwarding this to the guts for now.
    //The TrackSQL class has a different signature for
    //getPlayerLimits that actually has player limits in it
    //Keno only cares about the comp factor today.
    return  TrackSQL::NoError == guts->getPlayerLimits( info->pid, compFactor );
}

int32
GameConnection::trackGetDenominations()
{
	return guts->getDenominations();
}

int32
GameConnection::trackValidateLogin()
{
    pInfo info = (pInfo)casinoSpecific;
    int32 result = TrackSQL::ExecFailed;

    if (g_tracker && info)
    {
        g_tracker->lock();
		result = g_tracker->validateLogin(info->login, info->sessionId, info->pid);
        g_tracker->unlock();
    }

    return result;
}

int32
GameConnection::trackValidateSession()
{
    pInfo info = (pInfo)casinoSpecific;
    int32 result = TrackSQL::ExecFailed;

    if (g_tracker && info)
    {
        g_tracker->lock();
		result = g_tracker->validateSession(info->pid, info->sessionId);
        g_tracker->unlock();
    }

    return result;
}

void GameConnection::trackLogoutSession()
{
	pInfo info = (pInfo)casinoSpecific;
	
	if (g_tracker && info && !info->login.isEmpty())
    {
        g_tracker->lock();
		g_tracker->logout(ServerOpts::casinoId, info->login, info->forMoney, String::emptyString, info->sessionId);
        g_tracker->unlock();
    }
}

// kharmon.  08-13-07.  Added for Flash XML stuff.
void
GameConnection::allocateXMLTranslator( XMLTranslator** target )
{
	*target = new KenoXMLTranslator( getServer(), this );
}

// [AO 2008-01-16] Refactoring for HA
bool GameConnection::ValidateSession()
{
	int32 session_check = trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == TrackSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}

